import os
from threading import Thread
import time
from .request_base import Request
from .item import *
from .log import get_logger
from .exception_base import *
from .utiltiy import *
from .item_manager import *

class Downloader:
    def __init__(self,request:Request=None,logger=None,progress_changed_callback=None) -> None:
        self.request=request if request else Request()
        self.logger=logger if logger else get_logger('Downloader')
        self.progress_changed_callback=None
        self.failed_list=[]
        self.total_page=None
        self.total_chapter=None
        self.chapter_downloaded=None
        self.page_downloaed=0
        self.book_folder=None
        self.book_item=None
        # self.download_completed_callback=None

    def _progress_changed(self,book_id,progress:float):
        if self.progress_changed_callback:
            self.progress_changed_callback(book_id,progress)

    def _parse_book_arg(self,book):
        
        if isinstance(book,int):
            book_item=BookItem.get_or_none(book)
            if not book_item:
                raise ModelNotExistsException(f'Book:{book} not found!')
        elif isinstance(book,str):
            book_item=BookItem.get_or_none(BookItem.title==book)
            if not book_item:
                raise ModelNotExistsException(f'Book:{book} not found!')
        elif isinstance(book,BookItem):
            book_item=book
        else:
            raise Exception('Invalid argument type for book!')

        self.book_item=book_item
        return book_item


    def download_book(self,book:Union[int,str,BookItem],save_folder,force=False):

        book_item=self._parse_book_arg(book)
        ItemManager.update_item_state(book_item,ItemState.Downloading)
        self.total_page=book_item.total_page

        if not os.path.exists(save_folder):
            os.mkdir(save_folder)

        if force:
            ItemManager.reset_download_state(book_item)
            self.logger('Reset book downloading state!')

        self.logger.debug('Start downloading Book:'+book_item.title)

        if book_item.state==ItemState.Downloaded:
            self.logger.debug(f'Book[{book_item.title}] has already downloaded. If want to download again, please reset the download state!')
            return       


        book_folder=os.path.join(save_folder,clean_path(book_item.title))
        if not os.path.exists(book_folder):
            os.mkdir(book_folder)
        self.book_folder=book_folder

        for chapter_item in book_item.chapters:
            if chapter_item.state==ItemState.Downloaded:
                self.logger.debug(f'Chapter[{chapter_item.title}] already downloaded!')
                continue
            self.download_chapter(chapter_item,book_folder,book_item.chapter_count)
            ItemManager.update_item_state(chapter_item,ItemState.Downloaded)
            self.logger.debug(f'Download chapter[{chapter_item.title}] successfully!')

        ItemManager.update_item_state(book_item,ItemState.Downloaded)
        self.logger.debug('Download book successfully:'+book_item.title)
        if self.download_completed_callback:
            self.download_completed_callback()

    
    def download_chapter(self,chapter_item:ChapterItem,book_folder:str,chapter_count):
        order_len=len(str(chapter_count))
        chapter_name=str(chapter_item.order).rjust(order_len,'0')+' '+clean_path(chapter_item.title)
        chapter_folder=os.path.join(book_folder,chapter_name)
        if not os.path.exists(chapter_folder):
            os.mkdir(chapter_folder)
        downloaded=0
        page_order_len=len(str(chapter_item.page_count))
        for page_item in chapter_item.pages:
            if page_item.state==ItemState.Downloaded:
                self.logger.debug('Image has already downloaded!')
                downloaded+=1
                continue

            page_file_name=str(page_item.order).rjust(page_order_len,'0')+'.jpg'
            page_file_path=os.path.join(chapter_folder,page_file_name)
            if self.download_page(page_item,page_file_path):
                downloaded+=1
            chapter_progress=(page_item.order+1)/chapter_item.page_count
            progress=(chapter_item.order+chapter_progress)/chapter_count
            self._progress_changed(self.book_item.id,progress)

    def download_page(self,page_item,file_name):
        content=self.request.get(page_item.url).content
        if len(content)==0:
            self.logger.warn('Empty content when downloading page:'+page_item.url)
            return False
        with open(file_name,'wb') as f:
            f.write(content)
            ItemManager.update_item_state(page_item,ItemState.Downloaded)
            self.logger.debug('Download image successfully:'+page_item.url)
        return True

    def download_book_async(self,book,done_call_back=None):
        def download():
            self.download_book(book)
            if done_call_back:
                done_call_back()
        Thread(target=download).start()
        self.logger.debug(f'Async downloading is start.')
            
        
